﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace FerrScript
{
    internal class Parser
    {
        #region Fields
        private  static int          parenCount  = 0;
        private  static int          lineCount   = 0;
        internal static List<string> parseErrors = new List<string>();
        #endregion 

        #region Methods
        internal static ParseElement ParseScript(string       aScript)
        {
            parenCount = 0;
            lineCount  = 0;
            parseErrors.Clear();

            // add in a body, just in case the script doesn't already have one
            aScript = "body \n\r" + aScript + "\n\r)";

            StringReader sr = new StringReader(aScript);

            // read to the initial open paren
            ParseElement root = new ParseElement();
            ReadElement(root, sr);

            // check for missing parens
            if (parenCount > 0)
                parseErrors.Add("Not enough close parentheses! Missing " + parenCount);

            // check for any trailing code that may have been left out
            string rest = sr.ReadToEnd();
            rest.Trim();
            if (rest != "")
                parseErrors.Add("Too many close parentheses! Code is ignored after line " + (lineCount/2 + 1) + "!");

            // and if there are errors, we shouldn't reurn a proper value
            if (parseErrors.Count > 0)
                return null;
            return root;
        }
        private  static void         ReadElement(ParseElement aCurrentElement, StringReader aReader)
        {
            int                character   = aReader.Read();
            List<ParseElement> newElements = new List<ParseElement>();
            string             currValue   = "";

            while (true)
            {
                if (character == '(') // start an element
                {
                    parenCount++;
                    newElements.Add(new ParseElement());
                    ReadElement(newElements[newElements.Count - 1], aReader);
                }
                else if (character == ')' || character == '\0' || character == -1) // end an element
                {
                    if (currValue != "")
                    {
                        newElements.Add(new ParseElement(currValue));
                        currValue = "";
                    }

                    parenCount--;

                    aCurrentElement.value = newElements[0].value;
                    newElements.RemoveAt(0);
                    aCurrentElement.subElements = newElements;
                    return;
                }
                else if (character == ';') // comment
                {
                    while (character != '\n' && character != '\r')
                    {
                        character = aReader.Read();
                    }
                    lineCount++;
                }
                else if (character == ' ' || character == '\t' || character == '\n' || character == '\r') // whitespace
                {
                    if (currValue != "")
                    {
                        newElements.Add(new ParseElement(currValue));
                        currValue = "";
                    }
                    if (character == '\n' || character == '\r')
                    {
                        lineCount++;
                    }
                }
                else if (character == '\'') // string of text
                {
                    character = aReader.Read();
                    while (character != '\'' && character != '\0')
                    {
                        currValue += (char)character;
                        character = aReader.Read();
                    }
                    currValue = "\'" + currValue + "\'";
                }
                else // regular character
                {
                    currValue += (char)character;
                }

                character = aReader.Read();
            }

            throw new Exception("This should never be reached!");
        }
        #endregion
    }
}
